﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Serialization;
using System.IO;
using Microsoft.Xna.Framework.Input;

namespace Gamev1
{
    public class Level
    {
        #region Fields

        private int currentLevel;
        public List<Sprite> allSprites;
        public List<Bullet> bulletSprites;
        private List<Sprite> toBeRemovedList;
        private Game1 game;
        public Character character;
        private SpriteFont font;
        private Camera camera;
        public bool isGameOver { get; private set; }
        public bool hasWonGame { get; private set; }
        private Vector2 floorBackground;
        private Vector2 background;
        private Texture2D backgroundTexture;
        private Texture2D floorTexture;
        private HUD hud;
        private Random random = new Random();
        private XML xml;
        private int selectedCharacter;
        private List<Platform> middelPlatformen = new List<Platform>();

        #endregion

        public Level(Game1 game, int levelNumber, int characterNumber)
        {
            this.game = game;
            this.selectedCharacter = characterNumber;
            currentLevel = levelNumber;
            initializeLevel();
        }

        public Level()
        {

        }

        public void initializeLevel()
        {
            allSprites = new List<Sprite>();
            bulletSprites = new List<Bullet>();
            xml = new XML();
            loadLevel();
            camera = new Camera();
            floorBackground = new Vector2(0, -floorTexture.Height + Game1.screenHeight + character.Texture.Height);
            toBeRemovedList = new List<Sprite>();
        }

        #region Update & draw
        public void update(GameTime gameTime)
        {
            updateSprites(gameTime);
            checkCollisions();
            camera.Update(gameTime, character);
            hud.Update(gameTime);
            checkSpritesToBeRemoved();
            checkGameOver();
            UpdateBackground();
        }

        private void updateSprites(GameTime gameTime)
        {
            for (int index = 0; index < allSprites.Count; index++)
            {
                if (allSprites[index] is Boss)
                {
                    Boss boss = (Boss)allSprites[index];
                    updateBoss(boss);
                }
                allSprites[index].update(gameTime);
                handleInput();
                randomEnemyMovement(gameTime, index);
            }
        }

        private void randomEnemyMovement(GameTime gameTime, int index)
        {
            if (allSprites[index] is Enemy)
            {
                Enemy enemy = (Enemy)allSprites[index];
                int rnd = random.Next(10);
                enemy.moveRandomly(gameTime, rnd);
            }
        }

        private void updateBoss(Boss boss)
        {
            if (boss.canSeeCharacter && boss.CanHit)
            {
                Bullet bullet = boss.shoot(game.Content);
                allSprites.Add(bullet);
                bulletSprites.Add(bullet);
            }
        }

        private void UpdateBackground()
        {
            background = new Vector2(0, character.Position.Y - Game1.screenHeight + character.Rectangle.Height - 300);
            background.Y -= character.Position.Y * 0.05f;
        }

        private void checkSpritesToBeRemoved()
        {
            removeSprites();
            removeBullets();
            removeFromList();
        }

        public void draw(SpriteBatch spriteBatch)
        {
            spriteBatch.End();
            spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, null, null, camera.Transform);
            spriteBatch.Draw(backgroundTexture, background, Color.White);
            spriteBatch.Draw(floorTexture, floorBackground, Color.White);
            drawSprites(spriteBatch);
            hud.Draw(spriteBatch, font);
        }


        private void drawSprites(SpriteBatch spriteBatch)
        {
            foreach (Sprite sprite in allSprites)
            {
                sprite.draw(spriteBatch);
            }
        }

        #endregion

        #region Collision
        public void checkCollisions()
        {
            checkCharacterCollisions();
            checkEnemyFieldOfView();
            checkBulletCollisions();
        }

        public void checkCharacterCollisions()
        {
            for (int index = 0; index < allSprites.Count; index++)
            {
                if (character.isCollidingWith(allSprites[index]))
                {
                    character.collisionWith(allSprites[index]);
                    allSprites[index].collisionWith(character);
                }
            }
        }

        public void checkEnemyFieldOfView()
        {
            for (int index = 0; index < allSprites.Count; index++)
            {
                if (allSprites[index] is Enemy)
                {
                    Enemy enemy = (Enemy)allSprites[index];
                    if (enemy.FieldOfView.Intersects(character.Rectangle))
                    {
                        enemy.canSeeCharacter = true;
                    }
                    else
                    {
                        enemy.canSeeCharacter = false;
                    }
                }
            }
        }

        public void checkBulletCollisions()
        {
            for (int index_1 = 0; index_1 < bulletSprites.Count; index_1++)
            {
                for (int index_2 = 0; index_2 < allSprites.Count; index_2++)
                {
                    if (bulletSprites[index_1].isCollidingWith(allSprites[index_2]))
                    {
                        bulletSprites[index_1].collisionWith(allSprites[index_2]);
                        allSprites[index_2].collisionWith(bulletSprites[index_1]);
                    }
                }
            }
        }
        #endregion

        #region Methods

        private void handleInput()
        {
            if (character.CanShoot)
            {
                Bullet bullet = character.shoot(game.Content);
                allSprites.Add(bullet);
                bulletSprites.Add(bullet);
                character.CanShoot = false;
            }
        }

        public void checkGameOver()
        {
            if (!character.IsAlive || character.CurrentTime >= character.TimeLimit)
            {
                isGameOver = true;
            }
        }

        private void loadLevel()
        {
            allSprites = xml.load(currentLevel);
            loadMiddenPlatforms();
            loadEnemies();
            loadContent();
        }

        private void loadMiddenPlatforms()
        {
            foreach (Sprite sprite in allSprites)
            {
                if (sprite is Platform)
                {
                    if (sprite.TexturePath.EndsWith("_platformMiddel"))
                    {
                        Platform plat = (Platform)sprite;
                        plat.loadContent(game.Content);
                        middelPlatformen.Add(plat);
                    }
                }
            }
        }

        private void loadEnemies()
        {
            foreach (Sprite sprite in allSprites)
            {
                if (sprite is Boss)
                {
                    Enemy enemy = (Enemy)sprite;
                    enemy.loadContent(game.Content);
                    enemy.selectRandomPlatform(0);
                }
                else if (sprite is Enemy)
                {
                    int randomGetal = random.Next(middelPlatformen.Count);
                    Enemy enemy = (Enemy)sprite;
                    enemy.loadContent(game.Content);
                    Platform plat = middelPlatformen[randomGetal];
                    enemy.Position = new Vector2(plat.Position.X, plat.Position.Y - enemy.Texture.Height);
                    enemy.platformWidth = middelPlatformen[randomGetal].Texture.Width;
                    enemy.platformPosition = middelPlatformen[randomGetal].Position;
                    middelPlatformen.RemoveAt(randomGetal);
                    enemy.canDropKey = true;
                }
            }
        }

        private void loadContent()
        {
            foreach (Sprite sprite in allSprites)
            {
                if (sprite is Character)
                {
                    character = (Character)sprite;
                    selectCharacter();
                    hud = new HUD(character);
                    character.loadContent(game.Content);
                }
                else
                {
                    sprite.loadContent(game.Content);
                }
            }
            backgroundTexture = game.Content.Load<Texture2D>("Levels/achtergrond" + currentLevel);
            floorTexture = game.Content.Load<Texture2D>("Levels/OndergrondLevel" + currentLevel);
            hud.Texture = game.Content.Load<Texture2D>("Levels/HUD");
            font = game.Content.Load<SpriteFont>("SpriteFont");
        }

        private void removeSprites()
        {
            for (int index = 0; index < allSprites.Count; index++)
            {
                if (allSprites[index].canBeRemoved)
                {
                    if (allSprites[index] is Enemy)
                    {
                        Enemy enemy = (Enemy)allSprites[index];
                        checkEnemyDropsItem(enemy);
                        character.monstersKilled++;
                    }
                    if (allSprites[index] is Boss)
                    {
                        character.monstersKilled++;
                        hasWonGame = true;
                    }
                    toBeRemovedList.Add(allSprites[index]);
                    allSprites.Remove(allSprites[index]);
                }
            }
        }

        private void removeBullets()
        {
            for (int index = 0; index < bulletSprites.Count; index++)
            {
                if (bulletSprites[index].canBeRemoved)
                {
                    toBeRemovedList.Add(bulletSprites[index]);
                    bulletSprites.Remove(bulletSprites[index]);
                }
            }
        }

        private void checkEnemyDropsItem(Enemy enemy)
        {
            Random rand = new Random();
            int randomGetal = rand.Next(10);
            if (enemy.canDropKey && randomGetal >= 5 || enemy.canBeRemoved)
            {
                Item item = enemy.dropItem(game.Content);
                allSprites.Add(item);
            }
        }

        /// <summary>
        /// Garbage collector
        /// Zet sprite op null zodat de garbage collector deze sprites verwijderen
        /// </summary>
        private void removeFromList()
        {
            for (int index = 0; index < toBeRemovedList.Count; index++)
            {
                toBeRemovedList[index] = null;
                toBeRemovedList.RemoveAt(index);
            }
        }

        private void selectCharacter()
        {
            if (selectedCharacter == 1)
            {
                character.animationWalkPath = character.characterAnimationPath(4, "Characters/character_1_walking");
                character.animationJumpPath = character.characterAnimationPath(1, "Characters/character_1_jumping");
            }
            else if (selectedCharacter == 2)
            {
                character.animationWalkPath = character.characterAnimationPath(4, "Characters/character_2_walking");
                character.animationJumpPath = character.characterAnimationPath(1, "Characters/character_2_jumping");
            }
            else if (selectedCharacter == 3)
            {
                character.animationWalkPath = character.characterAnimationPath(4, "Characters/character_3_walking");
                character.animationJumpPath = character.characterAnimationPath(1, "Characters/character_3_jumping");
            }
        }
        #endregion
    }
}
